/* 
 * This program is free software; you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 * for more details.
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <QStringList>
#include <QMap>
#include <QRegExp>
#include "kusabaparser.h"
#include "log.h"
#include "text.h"
#include "timestampparser.h"

namespace Wishmaster
{
	class KusabaParserImpl
	{
	public:
		int parseOpPost(PostInfo& info, const QByteArray& input);
		ThreadInfo parseThreadEntry(const QString& chanName, const QString& boardId, const QString& str, int number);

		TimestampParser m_timestampParser;

		QString chanName;
		QString boardId;
	};

	KusabaParser::KusabaParser(const QString& chanName, const QString& boardname) : BoardParser()
	{
		m_impl = new KusabaParserImpl();
		m_impl->chanName = chanName;
		m_impl->boardId = boardname;
	}

	KusabaParser::~KusabaParser()
	{
		delete m_impl;
	}

	ThreadInfo KusabaParserImpl::parseThreadEntry(const QString& chanName, const QString& boardId, const QString& str, int number)
	{
		ThreadInfo info;
		info.thread_id = ThreadId();
		QString thread_regexp = "<a href=\"%BOARD%res/(\\d+)\\.html\">[^<]*</a>.*<blockquote[^>]*>(.*)</blockquote>";
		thread_regexp.replace(QString("%BOARD%"), boardId);
		QRegExp rx(thread_regexp, Qt::CaseSensitive, QRegExp::RegExp2);
		rx.setMinimal(true);
		int pos = 0;
		if(rx.indexIn(str, pos) == -1)
			return info;
		info.thread_id = ThreadId(chanName, boardId, rx.cap(1).toULong());
		info.topic = htmlToPlainText(rx.cap(2));
		if(info.topic.isEmpty())
		{
			info.topic = QString("#") + info.thread_id.threadStringId();
		}
		QRegExp img("<img src=\"([^\"]*)\"[^>]*class=\"thumb\"[^>]*>");
		QRegExp blockquote("<span class=\"postername\">[^<]*</span>([^<]*)</label>.*<blockquote[^>]*>");
		blockquote.setMinimal(true);

		int bqs = 0;
		pos = 0;
		int lastpos = pos;
		while((pos = blockquote.indexIn(str, pos)) >= 0)
		{
			bqs++;
			lastpos = pos;
			pos += blockquote.matchedLength();
		}

		blockquote.indexIn(str, lastpos);
		QDateTime threadtime = m_timestampParser.parse(blockquote.cap(1));
		info.timestamp = threadtime.toTime_t();

		//log(llDebug, "thread: %d, %s", info.thread_id.threadNumericId(), qPrintable(threadtime.toString()));

		QRegExp omittedPosts("<span class=\"omittedposts\">\\D*(\\d+)\\s");
		omittedPosts.setMinimal(true);
		if(omittedPosts.indexIn(str) > 0)
			info.posts = bqs + omittedPosts.cap(1).toInt();
		else
			info.posts = bqs;
		if(img.indexIn(str) > 0)
			info.leaderPicUrl = QUrl(img.cap(1));
		info.topic = info.topic.trimmed();
		if(img.indexIn(str) > 0)
			info.leaderPicUrl = QUrl(img.cap(1));
		return info;
	}

	QList<ThreadInfo> KusabaParser::getThreadList(const QString& chanName, const QString& boardId, const QByteArray& input)
	{
		QList<ThreadInfo> list;
		QString str = QString::fromUtf8(input);
		QRegExp hr("<\\s*hr[^>]*>");
		QStringList entries = str.split(hr);
		int i = 0;
		foreach(QString l, entries)
		{
			ThreadInfo info = m_impl->parseThreadEntry(chanName, boardId, l, i);
			if(info.thread_id.isValid())
				list.append(info);
			i++;
		}
		return list;
	}

	QList<PostInfo> KusabaParser::getPosts(const ThreadId& threadId, const QByteArray& input)
	{
		QList<PostInfo> list;
		QMap<PostId, int> postPositions;
		list.clear(); // just in case
		PostInfo oppost;
		int pos = m_impl->parseOpPost(oppost, input);
		
		if(pos == 0)
		{
			log(llDebug, "Error, no OP post found.");
			return list;
		}
		oppost.post_id = PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), threadId.threadNumericId());
		list.append(oppost);

		postPositions.insert(oppost.post_id, 0);

		QString thread = QString::fromUtf8(input);
		QString regexp = QString::fromUtf8("<a name=\"(\\d+)\">[^<]*</a>.*(?:<span class=\"filetitle\">(.*)</span>\\s+)?<span class=\"postername\">(<a href=\"[^\"]*\">)?([^<]*)(?:</a>)?</span>(.*)</label>\\s+<span class=\"reflink\">.*<span class=\"extrabtns\">\\s+</span>.*(?:javascript:expandimg\\('\\d+', '([^']*)', '([^']*)', '\\d+', '\\d+', '\\d+', '\\d+'\\).*)?<blockquote>(.*)</blockquote>.*</table>");

		QRegExp rx(regexp);
		rx.setMinimal(true);

		pos = 0;

		int post_position = 1;
		while((pos = rx.indexIn(thread, pos)))
		{
			if(pos == -1)
				break;
			pos += rx.matchedLength();
			PostInfo post;
			post.topic = htmlToPlainText(rx.cap(2)).trimmed();
			post.text = rx.cap(8).trimmed();
			post.name = htmlToPlainText(rx.cap(4)).trimmed();

			post.timestamp = m_impl->m_timestampParser.parse(rx.cap(5)).toTime_t();

			if(!rx.cap(6).isEmpty())
			{
				AttachmentInfo picture;
				picture.thumbUrl = QUrl(rx.cap(7));
				picture.url = QUrl(rx.cap(6));
				post.attachments.append(picture);
				//log(llDebug, "Kusaba parser: (%d) %s:%s", post.post_id, qPrintable(picture.url.toString()), qPrintable(picture.thumbUrl.toString()));
			}
			post.sage = rx.cap(3).contains("mailto:sage");
			post.post_id = PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), rx.cap(1).toULong());

			QList<PostId> links = findLinks(threadId, post.text);
			foreach(PostId link, links)
			{
				list[postPositions.value(link)].replies.insert(post.post_id);
			}
			
			list.append(post);

			postPositions.insert(post.post_id, post_position);
			post_position++;
		}
		return list;
	}

	PostId KusabaParser::getPostID(const QString& link, const ThreadId& threadId)
	{
		QString regexp = QString("%1res/%2\\.html#(\\d+)").arg(threadId.boardId()).arg(threadId.threadNumericId());
		QRegExp rx(regexp);
		if(rx.indexIn(link) != -1)
		{
			return PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), rx.cap(1).toULong());
		}
		rx.setPattern(QString("%1res/%2\\.html").arg(threadId.boardId()).arg(threadId.threadNumericId()));
		if(rx.indexIn(link) != -1)
			return PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), threadId.threadNumericId());
		return PostId();
	}

	QString KusabaParser::id()
	{
		return "kusaba";
	}

	int KusabaParserImpl::parseOpPost(PostInfo& info, const QByteArray& input)
	{
		QString thread = QString::fromUtf8(input);
		QString regexp = QString::fromUtf8("<form id=\"delform\".*(?:<span class=\"filetitle\">([^<]*)</span>.*)?<span class=\"postername\">(<a href=\"mailto:sage\">)?([^<]*)</span>(.*)</label>.*<blockquote>(.*)</blockquote>");
		QRegExp rx(regexp, Qt::CaseSensitive, QRegExp::RegExp);
		QRegExp imageRx("javascript:expandimg\\('\\d+', '([^']*)', '([^']*)'");
		rx.setMinimal(true);

		int pos = 0;
		pos = rx.indexIn(thread, pos);
		if(pos == -1)
		{
			return 0;
		}

		info.topic = htmlToPlainText(rx.cap(1)).trimmed();
		info.name = htmlToPlainText(rx.cap(3)).trimmed();
		info.text = rx.cap(5).trimmed();
		info.sage = rx.cap(2).contains("mailto:sage");
		info.timestamp = m_timestampParser.parse(rx.cap(4)).toTime_t();

		pos = imageRx.indexIn(thread);
		if(pos >= 0)
		{
			AttachmentInfo picture;
			picture.thumbUrl = QUrl(imageRx.cap(2));
			picture.url = QUrl(imageRx.cap(1));
			info.attachments.append(picture);
			log(llDebug, "Kusaba parser: (%s)(%s)", qPrintable(imageRx.cap(2)), qPrintable(imageRx.cap(1)));
		}
		return pos + rx.matchedLength();
	}

	QList<PostId> KusabaParser::findLinks(const ThreadId& threadId, const QString& posttext)
	{
		QList<PostId> links;
		QRegExp rx("onclick=\"(?:return |javascript:)highlight\\(\'(\\d+)\'");
		int pos = 0;
		while((pos = rx.indexIn(posttext, pos)) != -1)
		{
			links.append(PostId(threadId.chanName(), threadId.boardId(), threadId.threadNumericId(), rx.cap(1).toUInt()));
			pos += rx.matchedLength();
		}
		return links;
	}

	PostId KusabaParser::parseLink(const QString& url)
	{
		QRegExp rx("http://(.*)/(.*)/res/(\\d+)\\.html(?:#(\\d+))?$");
		if(rx.indexIn(url) < 0)
			return PostId();
		QString host = m_impl->chanName;
		return PostId(host, rx.cap(2).prepend('/').append('/'), rx.cap(3).toULong(), rx.cap(4).toULong());
	}

	BoardParser* KusabaParser::clone(const QString& chanName, const QString& boardname)
	{
		return new KusabaParser(chanName, boardname);
	}

	void KusabaParser::setTimestampFormat(const QString& format)
	{
		m_impl->m_timestampParser.setPattern(format);
	}
}


Q_EXPORT_PLUGIN2(kusabaparser, Wishmaster::KusabaParser)


